#include "Gamma.h"

Gamma::Gamma() { }

Gamma::~Gamma() { }

// devuelve una lista de bytes con la codificación en gamma de un numero
tListaBytes* Gamma::codificacion(int numero)
{
	bool vecPos[32];
	uint cantUnario, logX, nroBinario, cantBits, j;
	tListaBytes* res = new tListaBytes();

	for(j = 0; j < 32; j++)
		vecPos[j] = 0;

	logX = (uint)((log(numero)/log(2))+0.00000000001);
	cantUnario = 1 + logX;
	nroBinario = (uint)((numero) - (pow(2,logX)));
	cantBits = cantUnario + logX;

	if(cantBits <= 8){
		uchar codigo = 0;

		for(j=0; j<cantUnario-1; j++)
			(codigo |= (uint)(pow(2,(7-j))));

		ObtenerBitsPrendidos(vecPos,nroBinario);

		for(j=0; j<logX; j++)
			if(vecPos[31-j] == 1)
				(codigo |= (uint)(pow(2,(8-cantBits+j))));

		res->agregar(codigo);
	}
	else if((cantBits>8)&&(cantBits<=16)){
		uchar codigo1 = 0;
		uchar codigo2 = 0;

		for(j=0; j<cantUnario-1; j++)
			if(j<8)
				(codigo1 |= (uint)(pow(2,(7-j))));
			else
				(codigo2 |= (uint)(pow(2,(15-j))));

		ObtenerBitsPrendidos(vecPos,nroBinario);

		for(j=0; j<logX; j++)
			if(vecPos[31-j] == 1){
				if((16-cantBits+j)<8)
					(codigo2 |= (uint)(pow(2,(16-cantBits+j))));
				else
					(codigo1 |= (uint)(pow(2,(8-cantBits+j))));
			}
		res->agregar(codigo1);
		res->agregar(codigo2);
	}
	else if((cantBits>16)&&(cantBits<=24)){
		uchar codigo1 = 0;
		uchar codigo2 = 0;
		uchar codigo3 = 0;

		for(j=0; j<cantUnario-1; j++)
			if(j<8)
				(codigo1 |= (uint)(pow(2,(7-j))));
			else if((j>=8) && (j<16))
				(codigo2 |= (uint)(pow(2,(15-j))));
			else
				(codigo3 |= (uint)(pow(2,(23-j))));

		ObtenerBitsPrendidos(vecPos,nroBinario);

		for(j=0; j<logX; j++)
			if(vecPos[31-j] == 1){
				if((24-cantBits+j)<8)
					(codigo3 |= (uint)(pow(2,(24-cantBits+j))));
				else if(((24-cantBits+j)>=8) && ((24-cantBits+j)<16))
					(codigo2 |= (uint)(pow(2,(16-cantBits+j))));
				else
					(codigo1 |= (uint)(pow(2,(8-cantBits+j))));
			}

		res->agregar(codigo1);
		res->agregar(codigo2);
		res->agregar(codigo3);
	}
	else if((cantBits>24)&&(cantBits<=32)){
		uchar codigo1 = 0;
		uchar codigo2 = 0;
		uchar codigo3 = 0;
		uchar codigo4 = 0;

		for(j=0; j<cantUnario-1; j++)
			if(j<8)
				(codigo1 |= (uint)(pow(2,(7-j))));
			else if((j>=8) && (j<16))
				(codigo2 |= (uint)(pow(2,(15-j))));
			else if((j>=16) && (j<24))
				(codigo3 |= (uint)(pow(2,(23-j))));
			else
				(codigo4 |= (uint)(pow(2,(31-j))));

		ObtenerBitsPrendidos(vecPos,nroBinario);

		for(j=0; j<logX; j++)
			if(vecPos[31-j] == 1){
				if((32-cantBits+j)<8)
					(codigo4 |= (uint)(pow(2,(32-cantBits+j))));
				else if(((32-cantBits+j)>=8) && ((32-cantBits+j)<16))
					(codigo3 |= (uint)(pow(2,(24-cantBits+j))));
				else if(((32-cantBits+j)>=16) && ((32-cantBits+j)<24))
					(codigo2 |= (uint)(pow(2,(16-cantBits+j))));
				else
					(codigo1 |= (uint)(pow(2,(8-cantBits+j))));
			}
		res->agregar(codigo1);
		res->agregar(codigo2);
		res->agregar(codigo3);
		res->agregar(codigo4);
	}
	else
	{
		uchar codigo1 = 0;
		uchar codigo2 = 0;
		uchar codigo3 = 0;
		uchar codigo4 = 0;
		uchar codigo5 = 0;

		for(j=0; j<cantUnario-1; j++){
			if(j<8)
				(codigo1 |= (uint)(pow(2,(7-j))));
			else if((j>=8) && (j<16))
				(codigo2 |= (uint)(pow(2,(15-j))));
			else if((j>=16) && (j<24))
				(codigo3 |= (uint)(pow(2,(23-j))));
			else if((j>=24) && (j<32))
				(codigo4 |= (uint)(pow(2,(31-j))));
			else
				(codigo5 |= (uint)(pow(2,(39-j))));
		}

		ObtenerBitsPrendidos(vecPos,nroBinario);

		for(j=0; j<logX; j++)
			if(vecPos[31-j] == 1){
				if((40-cantBits+j)<8)
					(codigo5 |= (uint)(pow(2,(40-cantBits+j))));
				else if(((40-cantBits+j)>=8) && ((40-cantBits+j)<16))
					(codigo4 |= (uint)(pow(2,(32-cantBits+j))));
				else if(((40-cantBits+j)>=16) && ((40-cantBits+j)<24))
					(codigo3 |= (uint)(pow(2,(24-cantBits+j))));
				else if(((40-cantBits+j)>=24) && ((40-cantBits+j)<32))
					(codigo2 |= (uint)(pow(2,(16-cantBits+j))));
				else
					(codigo1 |= (uint)(pow(2,(8-cantBits+j))));
			}

		res->agregar(codigo1);
		res->agregar(codigo2);
		res->agregar(codigo3);
		res->agregar(codigo4);
		res->agregar(codigo5);
	}

	return res;
}

int Gamma::decodificacion(tListaBytes* codificacion){
	bool continuar;
	int c;
	int i;
	uchar byte=0,mod,cantBits;
	uint b;
	uint aux;
	uint mascara;
	int numeroDecodificado;

	continuar = true;
	c = 0;
	b = 0;
	mascara = 0;
	numeroDecodificado = 0;
	cantBits = 0;

	tIteradorListaBytes iteradorCod = codificacion->iterador();
	while(continuar && iteradorCod.tieneSiguiente()){
		//remueve el primer elemento y lo devuelve.
		byte = iteradorCod.remover();
		//byte = codificacion.front();
		//codificacion.pop_front();
		for(i = 0; i < 8; i++){
			mascara = (uint)(pow(2,(7-i)));
			if(byte & mascara)
				c++;
			else{
				continuar = false;
				break;
			}
		}
	}

	c++;
	mod = c%8;
	
	if(mod == 0)
		cantBits = c-1;
	else{
		for(i=0;i<(8-mod);i++){
			mascara = (uint)(pow(2,(7-mod-i)));
			if(byte & mascara){
				mascara = (uint)(pow(2,(c-2-i)));
				(b|=mascara);
			}
		}
		cantBits = c - 1 - (8-mod);
	}
	iteradorCod.primero();
	while((cantBits>0) && (cantBits<40) && iteradorCod.tieneSiguiente()){
		byte = iteradorCod.remover();
		//byte = codificacion.front();
		//codificacion.pop_front();
		for(i = 0; i < 8; i++){
			mascara = (uint)(pow(2,(7-i)));
			if(byte & mascara){
				mascara = (uint)(pow(2,(cantBits-1)));
				(b|=mascara);
			}
			cantBits--;
		}
	}

	aux = (uint)(pow(2,(c-1)));
	numeroDecodificado = aux + b;

	return numeroDecodificado;
}

void Gamma::ObtenerBitsPrendidos(bool vecPos[],uint nroBinario){
	uint i, res, aux;

	for(i=0; i<32; i++){
		aux = (uint)(pow(2,31-i));
		res = (nroBinario & aux);
		if(res == aux)
			vecPos[i] = 1;
	}
}
